package com.example.ssoreport.utils;

import lombok.extern.slf4j.Slf4j;

import javax.crypto.Cipher;
import javax.crypto.KeyGenerator;
import javax.crypto.SecretKey;
import javax.crypto.SecretKeyFactory;
import javax.crypto.spec.DESKeySpec;
import java.security.*;
import java.security.interfaces.RSAPrivateKey;
import java.security.interfaces.RSAPublicKey;
import java.security.spec.PKCS8EncodedKeySpec;
import java.security.spec.X509EncodedKeySpec;
import java.util.Base64;
import java.util.HashMap;
import java.util.Map;

@Slf4j
public class EncryptUtils {

    /**
     * sha 加密
     *
     * @param str 加密信息
     * @return 密文
     */
    public static String sha(String str) {
        String sha256Str = "";

        try {
            MessageDigest sha256Deget = MessageDigest.getInstance("SHA-256");
            byte[] sha256Encode = sha256Deget.digest(str.getBytes());
            sha256Str = ByteToHexStr(sha256Encode);
        } catch (Exception e) {
            log.info("FRLOG:SHA256加密异常:：", e.getMessage());
        }

        return sha256Str;
    }

    /**
     * byte数组转16进制字符串
     *
     * @param bytes 字节数组
     * @return byte数组转16进制字符串
     */
    private static String ByteToHexStr(byte[] bytes) {
        String hexStr = "";

        for (int i = 0; i < bytes.length; i++) {
            int temp = bytes[i] & 0xff;
            String tempHex = Integer.toHexString(temp);
            if (tempHex.length() < 2) {
                hexStr += "0" + tempHex;
            } else {
                hexStr += tempHex;
            }
        }

        return hexStr;
    }
/**--------------------------对称加密aes----------------------------------*/
    /**
     * aes 加密
     *
     * @param str        要加密字符串
     * @param privateKey 私钥
     * @return 密文
     */
    public static String aesEncrypt(String str, String privateKey) {
        try {
            // 生成密钥对象
            SecretKey secKey = generateAesKey(privateKey.getBytes());

            // 获取 AES 密码器
            Cipher cipher = Cipher.getInstance("AES");
            // 初始化密码器（加密模型）
            cipher.init(Cipher.ENCRYPT_MODE, secKey);

            // 加密数据, 返回密文
            byte[] cipherBytes = cipher.doFinal(str.getBytes());
//            return new BASE64Encoder().encodeBuffer(cipherBytes);
            return Base64.getEncoder().encodeToString(cipherBytes);
        } catch (Throwable e) {
            log.info("aes 加密异常", e.getMessage());
        }

        return null;
    }


    /**
     * aes 解密
     *
     * @param str        要解码的字符串
     * @param privateKey 私钥
     * @return 解码信息
     */
    public static String aesDecrypt(String str, String privateKey) {
        try {
            // 生成密钥对象
            SecretKey secKey = generateAesKey(privateKey.getBytes());

            // 获取 AES 密码器
            Cipher cipher = Cipher.getInstance("AES");
            // 初始化密码器（加密模型）
            cipher.init(Cipher.DECRYPT_MODE, secKey);
            byte[] decode = Base64.getDecoder().decode(str);
            // 加密数据, 返回密文
            byte[] cipherBytes = cipher.doFinal(decode);
            return new String(cipherBytes);
        } catch (Throwable e) {
            log.info("aes 解密异常 ", e.getMessage());
        }

        return null;
    }

    /**
     * 生成密钥对象
     */
    private static SecretKey generateAesKey(byte[] key) throws Exception {
        // 创建安全随机数生成器
        SecureRandom random = SecureRandom.getInstance("SHA1PRNG");
        // 设置 密钥key的字节数组 作为安全随机数生成器的种子
        random.setSeed(key);

        // 创建 AES算法生成器
        KeyGenerator gen = KeyGenerator.getInstance("AES");
        // 初始化算法生成器
        gen.init(128, random);

        // 生成 AES密钥对象, 也可以直接创建密钥对象: return new SecretKeySpec(key, ALGORITHM);
        return gen.generateKey();
    }
/**-------------------------- base64加密 ----------------------------------*/
    /**
     * base64加密
     *
     * @param key 要编码的字节数组
     * @return 包含生成的 Base64 编码字符的字符串
     */
    public static String base64Encode(byte[] key) {
        String result = Base64.getEncoder().encodeToString(key);
        return result;
    }


    /**
     * base64解密
     *
     * @param key 被加密信息
     * @return 加密信息
     */
    public static byte[] base64DecodeB(String key) {
        byte[] result = null;
        result = Base64.getDecoder().decode(key);
        return result;
    }

    /**
     * 是否被base64加密过
     *
     * @param str 被加密信息
     * @return true/false
     */
    public static boolean isBase64(String str) {
        if (str == null || str.trim().length() == 0) {
            return false;
        } else {
            if (str.length() % 4 != 0) {
                return false;
            }

            char[] strChars = str.toCharArray();

            for (char c : strChars) {
                if ((c >= 'a' && c <= 'z') || (c >= 'A' && c <= 'Z') || (c >= '0' && c <= '9') || c == '+' || c == '/' || c == '=') {
                    continue;
                } else {
                    return false;
                }
            }

            return true;
        }
    }
/**-------------------------- 对称加密des ----------------------------------*/
    /**
     * des加密
     *
     * @param datasource 被加密信息
     * @param password   秘钥
     * @return 加密后的信息
     */
    public static String desEncrypt(String datasource, String password) {
        try {
            SecureRandom random = new SecureRandom();
            DESKeySpec desKey = new DESKeySpec(password.getBytes());
            // 创建一个密匙工厂，然后用它把DESKeySpec转换成
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            SecretKey securekey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成加密操作
            Cipher cipher = Cipher.getInstance("DES");
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.ENCRYPT_MODE, securekey, random);
            // 现在，获取数据并加密
            // 正式执行加密操作
            return base64Encode(cipher.doFinal(datasource.getBytes()));
        } catch (Throwable e) {
            log.info("des 加密异常", e.getMessage());
        }

        return null;
    }

    /**
     * des 解密
     *
     * @param src      加密信息
     * @param password 秘钥
     * @return 解密后信息
     * @throws Exception
     */
    public static String desDecrypt(String src, String password) {
        try {
            // DES算法要求有一个可信任的随机数源
            SecureRandom random = new SecureRandom();
            // 创建一个DESKeySpec对象
            DESKeySpec desKey = new DESKeySpec(password.getBytes("UTF-8"));
            // 创建一个密匙工厂
            SecretKeyFactory keyFactory = SecretKeyFactory.getInstance("DES");
            // 将DESKeySpec对象转换成SecretKey对象
            SecretKey securekey = keyFactory.generateSecret(desKey);
            // Cipher对象实际完成解密操作
            Cipher cipher = Cipher.getInstance("DES");
            // 用密匙初始化Cipher对象
            cipher.init(Cipher.DECRYPT_MODE, securekey, random);
            // 真正开始解密操作
            return new String(cipher.doFinal(base64DecodeB(src)));
        } catch (Throwable e) {
            log.info("des 解密异常", e.getMessage());
        }
        return null;
    }
/**-------------------------- 非对称加密RSA ----------------------------------*/
    /**
     * 随机生成RSA密钥对
     *
     * @return privateKey, publicKey
     * @throws NoSuchAlgorithmException
     */
    public static Map<String, String> genRSAKeyPair() throws NoSuchAlgorithmException {
        // KeyPairGenerator类用于生成公钥和私钥对，基于RSA算法生成对象
        KeyPairGenerator keyPairGen = KeyPairGenerator.getInstance("RSA");
        // 初始化密钥对生成器，密钥大小为96-1024位
        keyPairGen.initialize(1024, new SecureRandom());
        // 生成一个密钥对，保存在keyPair中
        KeyPair keyPair = keyPairGen.generateKeyPair();
        // 得到私钥
        RSAPrivateKey privateKey = (RSAPrivateKey) keyPair.getPrivate();
        // 得到公钥
        RSAPublicKey publicKey = (RSAPublicKey) keyPair.getPublic();
        String publicKeyString = base64Encode(publicKey.getEncoded());
        // 得到私钥字符串
        String privateKeyString = base64Encode(privateKey.getEncoded());
        // 将公钥和私钥保存到Map
        Map<String, String> result = new HashMap<>(2);
        result.put("publicKey", publicKeyString.replaceAll("\n", "").replace("\r", "").trim());
        result.put("privateKey", privateKeyString.replaceAll("\n", "").replace("\r", "").trim());

        return result;
    }

    /**
     * rsa 加密
     *
     * @param str       被加密信息
     * @param publicKey 公钥
     * @return 加密后的信息
     * @throws Exception
     */
    public static String rsaEncrypt(String str, String publicKey) throws Exception {
        //base64编码的公钥
        byte[] decoded = base64DecodeB(publicKey);
        RSAPublicKey pubKey = (RSAPublicKey) KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(decoded));
        //RSA加密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.ENCRYPT_MODE, pubKey);
        String outStr = base64Encode(cipher.doFinal(str.getBytes("UTF-8")));
        return outStr;
    }

    /**
     * rsa解密
     *
     * @param str        加密信息
     * @param privateKey 私钥
     * @return 解密后信息
     * @throws Exception
     */
    public static String rsaDecrypt(String str, String privateKey) throws Exception {
        //64位解码加密后的字符串
        byte[] inputByte = base64DecodeB(str);
        //base64编码的私钥
        byte[] decoded = base64DecodeB(privateKey);
        RSAPrivateKey priKey = (RSAPrivateKey) KeyFactory.getInstance("RSA").generatePrivate(new PKCS8EncodedKeySpec(decoded));
        //RSA解密
        Cipher cipher = Cipher.getInstance("RSA");
        cipher.init(Cipher.DECRYPT_MODE, priKey);
        String outStr = new String(cipher.doFinal(inputByte));
        return outStr;
    }

    public static void main(String[] args) throws Exception {
        Map<String, String> keys = genRSAKeyPair();
        String publicKey = "MIGfMA0GCSqGSIb3DQEBAQUAA4GNADCBiQKBgQCVZPYtiTX4vp51XYsezqMMl9Ne+zR7DrfuRRmAXSV8yiu9ldeQUIZS5n43UsLvzXxk/OZg5BvZblNo5SsWib54KMnaPacbJQDIBVswLAs0Y1Czr5X7KYtMHGKvNBqpEtRSZXyVGLaW7l5cqW80o466E47CNv7tZJVLmoo0PlYitQIDAQAB";
        String privateKey = keys.get("privateKey");

        Map<String, java.io.Serializable> map = new HashMap<>(2);
        map.put("username", "xiamaofa");
        map.put("timestamp", 111111111);

        String encrypt = rsaEncrypt(map.toString(), publicKey);
        String decrypt = rsaDecrypt(encrypt, privateKey);

        System.out.println(encrypt);
    }
}
